
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include "ringbuffer.h"

#define ringbuffer_log(_fmt_, ...) \
        printf("[RB][%s:%d][%s] "_fmt_"\r\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#define min(a, b) (a < b ? a : b)

/**
 * @brief
 * @param  None
 * @retval None
 */
void ringbuffer_hexdump(ringbuffer_t *this, const void *data, size_t size, \
                        const char *str, ...)
{
    char t_str[128];
    char hexByte[4];
    char hexLine[50];
    char charByte[2];
    char charLine[18];

    printf("=========================================\r\n");
    printf("size %d\r\n", this->size);
    printf("mask 0x%x\r\n", this->mask);
    printf("in   %d\r\n", this->in);
    printf("out  %d\r\n", this->out);
    printf("frame %d\r\n", this->frame);

    va_list args;
    va_start(args, str);
    int len = vsnprintf(t_str, sizeof(t_str), str, args);
    va_end(args);
    printf("%.*s\r\n", len, t_str);

    for (uint32_t i = 0; i < size; i++) {
        if (i % 16 == 0) {
            hexLine[0] = 0;
            charLine[0] = 0;
        }
        if (i % 16 == 8) {
            strcat(hexLine, " ");
            strcat(charLine, " ");
        }
        snprintf(hexByte, sizeof(hexByte), " %02x", ((unsigned char *) data)[i]);
        strcat(hexLine, hexByte);
        if (((unsigned char *) data)[i] < 32 || ((unsigned char *) data)[i] > 126) {
            strcat(charLine, ".");
        } else {
            snprintf(charByte, sizeof(charByte), "%c", ((unsigned char *) data)[i]);
            strcat(charLine, charByte);
        }
        if (i % 16 == 15 || i == size - 1) {
            printf("%04x:%-49s  %s\r\n", i & ~15, hexLine, charLine);
        }
    }

    printf("=========================================\r\n");
}

static bool is_power_of_2(uint32_t x)
{
    return (x != 0) && ((x & (x - 1)) == 0);
}

int32_t ringbuffer_init(ringbuffer_t *this, uint8_t *buffer, uint32_t size, \
                        uint32_t format, uint32_t sample_rate, uint32_t channels)
{
    uint32_t splsize;

    if (!this) {
        return -1;
    }

    if (!is_power_of_2(size)) {
        return -1;
    }

    switch (format) {
        case AUIOD_FORMAT_S8:
        splsize = 1;
        break;

        case AUIOD_FORMAT_S16LE:
        case AUIOD_FORMAT_S16BE:
        splsize = 2;
        break;

        default:
        return -1;
    }

    this->buffer = buffer;
    this->size   = size;
    this->mask   = size - 1;
    this->in     = 0;
    this->out    = 0;
    memset(this->buffer, 0, size);

    this->audio.format = format;
    this->audio.sample_rate = sample_rate;
    this->audio.channels = channels;

    this->frame = splsize * channels;
    return 0;
}

int32_t ringbuffer_deinit(ringbuffer_t *this)
{
    return 0;
}

uint32_t ringbuffer_free_space(ringbuffer_t *this)
{
    return ((this->mask - this->in + this->out) & this->mask);
}

uint32_t ringbuffer_used_space(ringbuffer_t *this)
{
    return ((this->size - this->out + this->in) & this->mask);
}

int32_t ringbuffer_write_frames(ringbuffer_t *this, uint8_t *data, uint32_t frames)
{
    uint32_t csmsize = 0, in_end_size;
    uint32_t cpysize = frames * this->frame;

    if (ringbuffer_free_space(this) < cpysize) {
        csmsize = cpysize - ringbuffer_free_space(this);
    }

    if (csmsize) {
        this->out = (this->out + csmsize) & this->mask;
    }

    in_end_size = this->size - this->in;

    memcpy(&this->buffer[this->in], data, min(in_end_size, cpysize));

    if (in_end_size < cpysize) {
        memcpy(&this->buffer[0], data + in_end_size, cpysize - in_end_size);
    }

    this->in = (this->in + cpysize) & this->mask;
    return cpysize;
}

#if 0
int32_t ringbuffer_get_data(ringbuffer_t *this, unsigned char** data, uint32_t* contiguous_bytes)
{
    uint32_t head_to_end = this->size - this->in;

    *data = &this->buffer[this->in];
    *contiguous_bytes = min(head_to_end, (head_to_end + this->out) % this->size);
    return 0;
}

int32_t ringbuffer_consume(ringbuffer_t *this, uint32_t bytes_consumed)
{
    this->in = (this->in + bytes_consumed) % this->size;
    return 0;
}

signed int ringbuffer_write(ringbuffer_t *this, const unsigned char* data, uint32_t data_length)
{
    uint32_t tail_to_end = this->size-1 - this->out;

    /* Calculate the maximum amount we can copy */
    uint32_t amount_to_copy = min(data_length, (tail_to_end + this->in) % this->size);

    /* Fix the bug when out is at the end of buffer */
    tail_to_end++;

    /* Copy as much as we can until we fall off the end of the buffer */
    memcpy(&this->buffer[this->out], data, min(amount_to_copy, tail_to_end));

    /* Check if we have more to copy to the front of the buffer */
    if ( tail_to_end < amount_to_copy )
    {
        memcpy(&this->buffer[ 0 ], data + tail_to_end, amount_to_copy - tail_to_end);
    }

    /* Update the out */
    this->out = (this->out + amount_to_copy) % this->size;

    return amount_to_copy;
}

unsigned char ringbuffer_is_full(ringbuffer_t *this)
{
    if (ringbuffer_used_space(this) >= this->size - 1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

int32_t ringbuffer_reset(ringbuffer_t *this)
{
    if (this)
    {
        this->in    = 0;
        this->out    = 0;
        return 0;
    }
    else
    {
        return -1;
    }
}
#endif

/* End of file */



